import { keys, trim } from "lodash-es";
import util, { dom, getOuterHTML, style } from "../util";
import { ignoredAttrs } from "./attrs";
import type { Node } from "../util/dom";

const renameTags = [
  'view',
  'scroll-view',
  'swiper',
  'swiper-item',
  'image',
  'text',
  'rich-text',
  'progress',
  'button',
  'checkbox-group',
  'checkbox',
  'form',
  'input',
  'label',
  'picker',
  'picker-view',
  'radio-group',
  'radio',
  'switch',
  'textarea',
  'navigator',
  'audio',
  'image',
  'video',
  'map'
];

const twoWayBindingConfigs = {
  input: { wxAttrName: 'value' },
  textarea: { wxAttrName: 'textarea' },
  switch: { wxAttrName: 'checked', uiAttrName: 'value' },
  'ui-popup': { wxAttrName: 'show', uiAttrName: 'value' },
  'ui-mask': { wxAttrName: 'show', uiAttrName: 'value' }
};
const adjustmentTags = [
  'progress',
  'button',
  'form',
  'input',
  'textarea',
  'navigator'
];
const uiForAssits = ['ui:for-item', 'ui:for-index', 'ui:key'];

export class WxTemplate {

  dom!: Node[];
  directiveElems!: any[];
  attrToStyleElems!: any[];
  bindEventsElems!: any[];
  styleElems!: any[];
  customStyleElems!: any[];
  wechatIconElems!: any[];
  twoWayBindingElems!: any[];
  adjustmentElems!: any[];
  renameElems!: any[];

  constructor(public source: string, public isApp?: Boolean | undefined) {
    this.initDom();
  }

  initDom() {
    if (!this.source) return;
    this.dom = dom.make(this.source);
    this.directiveElems = dom.getElementsByAttrRegex(
      this.dom,
      /(^wx:|hidden)/
    );
    this.bindEventsElems = dom.getElementsByAttrRegex(
      this.dom,
      /^bind/,
      /^\w+\((.*)\)/
    );
    this.styleElems = dom.getElementsByAttrRegex(this.dom, /^style$/);
    this.customStyleElems = dom.getElementsByAttrRegex(
      this.dom,
      /-style/
    );
    this.wechatIconElems = dom.getElementsByTagName(this.dom, 'icon');
    this.twoWayBindingElems = dom.getElementsByTagName(
      this.dom,
      Object.keys(twoWayBindingConfigs)
    );
    this.adjustmentElems = dom.getElementsByTagName(
      this.dom,
      adjustmentTags
    );
    this.renameElems = dom.getElementsByTagName(this.dom, renameTags);
  }

  async generator() {
    let code = '';
    if (!this.dom) return code;
    this.transformWxDirective();
    this.transformIcons();
    this.transformTwoWayBinding();
    this.adjustAttrs();
    this.transformWxTag2UiTag();
    this.handleRootElement();
    code = getOuterHTML(this.dom);
    return code;
  }

  transformWxDirective() {
    this.directiveElems.forEach((elem) => {
      Object.keys(elem.attribs).forEach((key) => {
        let value = elem.attribs[key];
        let newKey;
        let change = true;
        if (key === 'hidden') {
          newKey = 'ui:show';
          let match = value.match(/\{\{\s*(.*)\s*\}\}/);
          if (match) {
            if (match[1].startsWith('!')) {
              value = `{{ ${match[1].substring(1)} }}`;
            } else {
              value = /(\|\||&&|>=?|<=?|===?)/.test(match[1])
                ? `{{ !(${match[1]}) }}`
                : `{{ !${match[1]} }}`;
            }
          } else {
            if (value === '') {
              value = '{{ false }}';
            } else {
              if (value[0].startsWith('!')) {
                value = `{{ ${value.substring(1)} }}`;
              } else {
                value = `{{ !${value} }}`;
              }
            }
          }
        } else if (/^wx:/.test(key)) {
          newKey = key.replace('wx:', 'ui:');
          if (uiForAssits.indexOf(newKey) < 0) {
            value = util.getMustcacheValue(value);
          }
        } else {
          change = false;
        }
        if (newKey && change) {
          elem.attribs[newKey] = value;
          delete elem.attribs[key];
        }
      });
    });
  }

  transformEventArgs() {
    this.bindEventsElems.forEach((elem) => {
      keys(elem.attribs).forEach((key) => {
        if (/^bind/.test(key)) {
          let matches = elem.attribs[key].match(/^(\w+)\((.*)\)/);
          if (matches && matches[2]) {
            let args = matches[2].split(',').map((arg: string) => trim(arg));
            args.forEach((arg: string, index: number) => {
              elem.attribs['data-arg' + index] = `{{ ${args[index]} }}`;
            });
            elem.attribs[key] = matches[1];
          }
        }
      });
    });
  }

  transformBraceStyle() {
    this.styleElems.forEach((elem) => {
      style.uiStyle2wxStyle(elem);
    });
    this.customStyleElems.forEach((elem) => {
      style.reviseBrace(elem);
    });
  }
  transformIcons() {
    this.wechatIconElems.forEach((elem) => {
      elem.name = 'ui-icon';
      elem.attribs.mode = 'wechat';
    });
  }

  transformTwoWayBinding() {
    this.twoWayBindingElems.forEach((elem) => {
      // @ts-ignore
      let wxAttrName = twoWayBindingConfigs[elem.name].wxAttrName;
      // @ts-ignore
      let uiAttrName = twoWayBindingConfigs[elem.name].uiAttrName;
      if (/\{\{/.test(elem.attribs[wxAttrName])) {
        elem.attribs['ui:model'] = elem.attribs[wxAttrName];
        delete elem.attribs[wxAttrName];
      } else if (uiAttrName) {
        let value = elem.attribs[wxAttrName];
        elem.attribs[uiAttrName] = value === '' ? '{{ true }}' : value;
        delete elem.attribs[wxAttrName];
      }
    });
  }

  adjustAttrs() {
    this.adjustmentElems.forEach((elem) => {
      if (elem.name === 'input' && elem.attribs.password === '') {
        elem.attribs.type = 'password';
        delete elem.attribs.password;
      }
      // @ts-ignore
      let attrs = ignoredAttrs.wx2ui[elem.name];
      keys(elem.attribs).forEach((attr) => {
        if (attrs.indexOf(attr) > -1) {
          delete elem.attribs[attr];
        }
      });
    });
  }
  transformWxTag2UiTag() {
    this.renameElems.forEach((elem) => {
      elem.name = `ui-${elem.name}`;
    });
  }

  handleRootElement() {
    if (dom.isOneRootDom(this.dom)) {
      for (let elem of this.dom) {
        if (elem.type === 'tag') {
          elem.name = 'ui-page';
          break;
        }
      }
    } else {
      this.dom = [
        {
          // @ts-expect-error make similar element
          type: 'tag',
          name: 'ui-page',
          children: this.dom
        }
      ]
    }
  }

}
